#!/usr/bin/env python
# -*- coding: ISO-8859-15 -*-
#
# Copyright (C) 2007 Rafael S. Gonzalez D'Leon <rafaeldleon@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA

import sha
import binascii

from sshproxy.config import get_config
from sshproxy.client import ClientDB, ClientInfo
from sshproxy.util import istrue
from sshproxy.server import Server

from config import LDAPConfigSection
from ldapops import LDAPDB

class LDAPClientConfigSection(LDAPConfigSection):
    section_id = 'client_db.ldap'

LDAPClientConfigSection.register()


class LDAPClientInfo(ClientInfo, LDAPDB):
    _db_handler = 'client_db'
    def __reginit__(self, username, **tokens):
        self.open_db()
        ClientInfo.__reginit__(self, username, **tokens)


    def load(self):
        
        query = '(&(ObjectClass=sshProxyClient)(cn=%s))' % self.username
        result = self.ldap_get(query)
        if not result:
            return
        userdn, userattrs = result
        self._id = userdn
        userpass_ready=False
        try: 
            password = userattrs['userPassword'][0]
            userpass_ready=True
        except:
            pass
        pkey_ready= False
        try:
            pkey = userattrs['sshPublicKey'][0]
            pkey_ready= True
        except:
            pass
        tokensdict={}
        if userpass_ready:
            if pkey_ready:
                self.load_tags(self._id, password=password,pkey=pkey)
            else:
                self.load_tags(self._id, password=password)
        elif pkey_ready:
            self.load_tags(self._id, pkey=pkey)
        else:
            self.load_tags(self._id)
        
    def load_tags(self, id=None, **tokens):
        if id is None:
            id = self._id
        if id is None:
            return
        query = '(&(ObjectClass=sshProxyClient)(cn=%s))' % self.username
       
        tags = tokens
        result = self.ldap_get(query)
        userdn, userattrs = result
        try: #This try was added to workaround an error passing by sshProxyTags when there isn't anyone
            acltags = userattrs['sshProxyTags']
            for tagentry in acltags:
                tag,value = tagentry.split(' = ')
                
                if value.startswith('--'):
                    newvalue=value.split('--')[1]
                    tagstodelete =[('sshProxyTags',tag+' = '+newvalue)]
                    self.ldap_del_attrs(userdn,tagstodelete)
                elif len(value):
                    if tags.has_key(tag):
                        tags[tag]=tags[tag]+' '+value
                    else:
                        tags[tag] = value
                else:
                    tagstodelete=[]
                    for tagentrytemp in acltags:
                        tagtemp,valuetemp = tagentrytemp.split(' = ')
                        if tagtemp == tag:
                            tagstodelete.append(('sshProxyTags',tagentrytemp))
                    
                    self.ldap_del_attrs(userdn,tagstodelete)
        except:
            pass

        self.set_tokens(**tags)

    def save(self):
        id = self._id
        if id is None:
            return
        for tag, value in self.tokens.items():
            if tag == 'username':
                continue
            elif tag == 'password':
               
                if str(value).startswith('{SHA}'):
                    self.ldap_update(id,[('userPassword', str(value))])
                    
                else:
                    self.ldap_update(id,[('userPassword','{SHA}'+binascii.a2b_hex(str(value)).encode('base64').split('\n')[0])])
                    
            elif tag == 'pkey':
                modlist = [('objectClass',['ldapPublicKey'])]
                modlist.append(('sshPublicKey', str(value)))
                modlist.append(('uid', self.username))
                self.ldap_update(id,modlist)
            elif value.startswith('--'):
                newvalue=value.split('--')[1]
                tagstodelete =[('sshProxyTags',tag+' = '+newvalue)]
                self.ldap_del_attrs(id,tagstodelete)
            
            elif value and len(str(value)):
                valuelist=value.split(' ')
                for onevalue in valuelist:
                    self.ldap_update(id,[('sshProxyTags',tag+' = '+onevalue)])
                
            else:
                
                tagstodelete=[]
                result = self.ldap_get('(objectClass=*)',id)
                proxytagstemp = result[1]['sshProxyTags']
                for tagentrytemp in proxytagstemp:
                    tagtemp,valuetemp = tagentrytemp.split(' = ')
                    
                    if tagtemp == tag:
                        tagstodelete.append(('sshProxyTags',tagtemp+' = '+valuetemp))
                
                self.ldap_del_attrs(id,tagstodelete)
              

    def auth_token_order(self):
        return ('pkey', 'password')


    def authenticate(self, **tokens):
        resp = False
        for token in self.auth_token_order():
            if token in tokens.keys() and tokens[token] is not None:
                if token == 'password':
                    query = '(&(ObjectClass=sshProxyClient)(cn=%s))' % self.username
                    result = self.ldap_get(query)
                    if not result:
                        return
                    userdn, userattrs = result
                    
                    password = userattrs['userPassword'][0]
                    
                            
                    if password == '{SHA}'+sha.new(tokens['password']).digest().encode('base64').split('\n')[0]:
                        resp = True
                        break
                elif token == 'pkey':
                    pkeys = self.get_token(token, '').split('\n')
                    pkeys = [ pk.split()[0] for pk in pkeys if len(pk) ]
                    for pk in pkeys:
                        if pk == tokens[token]:
                            resp = True
                            break
                    ClientDB()._unauth_pkey = tokens[token]

                elif self.get_token(token) == tokens[token]:
                    resp = True
                    break
        pkey = getattr(ClientDB(), '_unauth_pkey', None)
        if resp and pkey and istrue(get_config('sshproxy')['auto_add_key']):
            tokens['pkey'] = pkey
            if self.add_pkey(**tokens):
                Server().message_client("WARNING: Your public key"
                                        " has been added to the keyring\n")
            del ClientDB()._unauth_pkey
        return resp


class LDAPClientDB(ClientDB, LDAPDB):
    _db_handler = 'client_db'
    def __reginit__(self, **tokens):
        self.open_db()
        ClientDB.__reginit__(self, **tokens)

    def exists(self, username, **tokens):
        query = '(&(ObjectClass=sshProxyClient)(cn=%s))' % username
        
        try:
            id = self.ldap_get(query)[0]
        except:
            return False
        if id:
            return id
        return False

    def add_client(self, username, **tokens):
        if self.exists(username, **tokens):
            return 'Client %s does already exist' % username
        cfg = get_config('%s.ldap' % self._db_handler)
        base_dn = cfg['base_dn']
        userdn = 'cn=%s,'% username + base_dn
        
        modlist = [('objectClass',['inetOrgPerson', 'top', 'sshProxyClient']),('cn',username),('sn',username)]
        try:
            self.ldap_add(userdn,modlist)
        except:
            return 'A problem occured when adding client %s' % username
        
        client = ClientInfo(username, **tokens)
        client.save()
        return 'Client %s added' % username

    def del_client(self, username, **tokens):
        if self.clientinfo.username == username:
            return "Don't delete yourself!"
        id = self.exists(username, **tokens)
        if not id:
            return 'Client %s does not exist' % username
        
        try:
            self.ldap_del(id)
        except:
            return 'A problem occured when deleting client %s' % username
        
        return 'Client %s deleted' % username

    def list_clients(self, **kw):
        query = '(ObjectClass=sshProxyClient)'
        
        try:
            listusers=self.ldap_list(query)
        except:
            return 'An error ocurred when getting users'
        result = []
        for userdn, userattrs in listusers:
            result.append(userattrs['cn'][0])
        return result
        
